package com.freemalll.merchant.utils;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * DTO 字段对比工具类，支持嵌套 DTO。
 */
@Slf4j
public class DtoFieldComparatorUtil {

    private static final String MARK_DIFFERENCES = "MARK_DIFFERENCES";

    /**
     * @param dtoList
     * @param <T>
     * @return
     */
    public static <T> List<Map<String, Object>> convertDTOListToMapList(List<T> dtoList) {
        List<Map<String, Object>> resultList = new ArrayList<>();
        if (dtoList == null || dtoList.isEmpty()) {
            return resultList;
        }
        // 获取第一个 DTO 的类信息，用于确定所有属性
        Class<?> clazz = dtoList.get(0).getClass();
        Field[] fields = clazz.getDeclaredFields();
        // 为每个属性创建一个 Map，收集所有 DTO 中该属性的值
        for (Field field : fields) {
            field.setAccessible(true); // 允许访问私有字段
            if (field.isAnnotationPresent(SkipComparison.class)) {
                continue;
            }
            Map<String, Object> propertyMap = new HashMap<>();
            String name = field.getName();
            if (field.isAnnotationPresent(ColumnName.class)) {
                String columnName = field.getAnnotation(ColumnName.class).value();
                propertyMap.put("name", columnName);
                propertyMap.put("key", name);
            }
            propertyMap.put("modelName", determineModelName(name));

            for (T dto : dtoList) {
                try {
                    // 获取 ID 值
                    Field idField = clazz.getDeclaredField("sourceId");
                    idField.setAccessible(true);
                    if (field.isAnnotationPresent(SkipComparison.class)) {
                        continue;
                    }
                    // 获取属性值
                    Object value = field.get(dto);
                    String sourceId = (String) idField.get(dto);
                    // 获取场景分类
                    Field supportingScenarios = clazz.getDeclaredField("supportingScenarios");
                    supportingScenarios.setAccessible(true);
                    String supportingS = (String) supportingScenarios.get(dto);
                    // 构建键：属性名_ID
                    String key = sourceId + "_" + supportingS;
                    // 如果是offlineRepaymentRate字段，值后面添加%
                    if ("offlineRepaymentRate".equals(field.getName()) && value != null) {
                        value = value + "%";
                    }
                    // 存入 Map
                    propertyMap.put(key, value == null || value.equals("") ? "-" : value.toString());
                } catch (Exception e) {
                    log.error("Error processing field: {} - {}", field.getName(), e.getMessage());
                }
            }
            if (!propertyMap.isEmpty()) {
                resultList.add(propertyMap);
            }
        }
        return resultList;
    }

    public static <T> List<Map<String, Object>> convertDTOListToMapListDiff(List<T> dtoList) {
        List<Map<String, Object>> resultList = new ArrayList<>();
        if (dtoList == null || dtoList.isEmpty()) {
            return resultList;
        }

        Class<?> clazz = dtoList.get(0).getClass();
        Field[] fields = clazz.getDeclaredFields();

        // 首先标记相同字段为MARK_DIFFERENCES
        markSameFieldsAsDifferences(dtoList);

        for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(SkipComparison.class)) {
                continue;
            }

            // 检查该字段是否所有DTO都为MARK_DIFFERENCES
            boolean allSame = true;
            try {
                for (T dto : dtoList) {
                    if (!MARK_DIFFERENCES.equals(field.get(dto))) {
                        allSame = false;
                        break;
                    }
                }
            } catch (IllegalAccessException e) {
                log.error("Error accessing field: {}", field.getName(), e);
                continue;
            }

            // 如果字段值不完全相同，则创建属性Map
            if (!allSame) {
                Map<String, Object> propertyMap = new HashMap<>();
                String name = field.getName();

                // 添加字段元信息
                if (field.isAnnotationPresent(ColumnName.class)) {
                    propertyMap.put("name", field.getAnnotation(ColumnName.class).value());
                }
                propertyMap.put("key", name);
                propertyMap.put("modelName", determineModelName(name));

                // 添加各DTO的字段值
                for (T dto : dtoList) {
                    try {
                        // 获取ID和场景分类
                        Field idField = clazz.getDeclaredField("sourceId");
                        idField.setAccessible(true);
                        Field supportingScenarios = clazz.getDeclaredField("supportingScenarios");
                        supportingScenarios.setAccessible(true);

                        String sourceId = (String) idField.get(dto);
                        String supportingS = (String) supportingScenarios.get(dto);
                        String key = sourceId + "_" + supportingS;

                        // 获取字段值
                        Object value = field.get(dto);
                        if (MARK_DIFFERENCES.equals(value)) {
                            continue;
                        }

                        // 特殊处理offlineRepaymentRate字段
                        if ("offlineRepaymentRate".equals(name) && value != null) {
                            value = value + "%";
                        }

                        propertyMap.put(key, value == null || value.equals("") ? "-" : value.toString());
                    } catch (Exception e) {
                        log.error("Error processing field: {} - {}", field.getName(), e.getMessage());
                    }
                }

                if (!propertyMap.isEmpty()) {
                    resultList.add(propertyMap);
                }
            }
        }

        return resultList;
    }

    private static String determineModelName(String fieldName) {
        if (checkFieldsInDetails(fieldName, "cooperationStatus", "pushOrderStatus", "dailyLimit", "monthlyLimit")) {
            return "商户管理";
        }
        if (checkFieldsInDetails(fieldName, "payType", "interestFeeSupport", "rateRange")) {
            return "场景息费";
        }
        if (checkFieldsInDetails(fieldName, "ageRange", "genderRestrictions", "ethnicRestrictions",
                "occupationalRestrictions", "projectConstraints", "creditRange",
                "supportPeriods", "creditTimeframe", "lendingTimeframe", "banPeriod", "twoOrderRestriction", "customerRegionalRestrictions")) {
            return "客户准入";
        }
        if (checkFieldsInDetails(fieldName, "merchantApproval", "establishmentDuration", "contractSigning",
                "regionalRestrictions", "activityReporting", "merchantInspection", "merRegionalRestrictions")) {
            return "商户准入";
        }
        if (checkFieldsInDetails(fieldName, "compensationFlag", "compensationStandard", "buyBackFlag",
                "buyBackStandard", "offlineRepayment", "offlineRepaymentRate")) {
            return "贷后信息";
        }
        return fieldName;
    }


    private static boolean checkFieldsInDetails(String key, String... requiredKeys) {
        return requiredKeys != null && Arrays.asList(requiredKeys).contains(key);
    }


    /**
     * 标记列表中所有DTO的相同字段为MARK_DIFFERENCES
     *
     * @param dtoList DTO列表
     * @param <T>     DTO类型
     * @return 处理后的DTO列表
     */
    public static <T> void markSameFieldsAsDifferences(List<T> dtoList) {
        if (dtoList == null || dtoList.isEmpty()) {
            return;
        }

        Class<?> clazz = dtoList.get(0).getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(SkipComparison.class)) {
                continue;
            }

            // 检查该字段在所有DTO中是否相同
            boolean allSame = true;
            Object firstValue;
            try {
                firstValue = field.get(dtoList.get(0));
                for (int i = 1; i < dtoList.size(); i++) {
                    if (!areEqual(firstValue, field.get(dtoList.get(i)))) {
                        allSame = false;
                        break;
                    }
                }

                // 如果所有值相同，则设置为MARK_DIFFERENCES
                if (allSame) {
                    for (T dto : dtoList) {
                        field.set(dto, MARK_DIFFERENCES);
                    }
                }
            } catch (IllegalAccessException e) {
                log.error("Error accessing field: {}", field.getName(), e);
            }
        }
    }

    // 判断两个值是否相等（兼容 null 和不同类型数值比较）
    private static boolean areEqual(Object val1, Object val2) {
        // 处理null和空字符串比较
        if (val1 == null || val2 == null ||
                (val1 instanceof String && ((String) val1).isEmpty()) ||
                (val2 instanceof String && ((String) val2).isEmpty())) {
            // 以下情况返回true:
            // 1. 两者都为null
            // 2. 两者都为空字符串
            // 3. 一个为null，另一个为空字符串
            return (val1 == null && val2 == null) ||
                    (val1 instanceof String && ((String) val1).isEmpty() &&
                            val2 instanceof String && ((String) val2).isEmpty()) ||
                    (val1 == null && val2 instanceof String && ((String) val2).isEmpty()) ||
                    (val2 == null && val1 instanceof String && ((String) val1).isEmpty());
        }

        // 如果都是 String 并且包含逗号，则按逗号拆分成 List 比较
        if (val1 instanceof String && val2 instanceof String) {
            String str1 = (String) val1;
            String str2 = (String) val2;

            if (str1.contains(",") || str2.contains(",")) {
                List<String> list1 = Arrays.stream(str1.split(","))
                        .map(String::trim)
                        .sorted()
                        .collect(Collectors.toList());

                List<String> list2 = Arrays.stream(str2.split(","))
                        .map(String::trim)
                        .sorted()
                        .collect(Collectors.toList());

                return list1.equals(list2);
            }

            return str1.equals(str2);
        }

        if (val1 instanceof Number && val2 instanceof Number) {
            return ((Number) val1).doubleValue() == ((Number) val2).doubleValue();
        }

        return val1.equals(val2);
    }
}
